package com.zfk.framework.utils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zfk.framework.models.CaseData;
import com.zfk.framework.models.ResponseData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Map;

import static org.testng.Assert.*;

/**
 * 断言工具类，用于验证API响应是否符合预期结果
 */
public class JsonBodyAssertionUtils {
    private static final Logger logger = LogManager.getLogger(JsonBodyAssertionUtils.class);
    private static final ObjectMapper mapper = new ObjectMapper();

    /**
     * 根据测试用例中的预期结果验证响应
     *
     * @param caseData 测试用例数据
     * @param response 实际响应数据
     */
    public static void assertResponse(CaseData caseData, ResponseData response) {
        try {
            Map<String, Object> expected = caseData.getExpected();
            if (expected != null && !expected.isEmpty()) {
                // 检查是否包含jsonBody字段
                if (!expected.containsKey("jsonBody")) {
                    logger.error("测试用例 {} 的expected字段中必须包含jsonBody子字段", caseData);
                    throw new IllegalArgumentException("测试用例的expected字段中必须包含jsonBody子字段");
                }
                
                // 获取jsonBody字段的值
                Object jsonBodyExpected = expected.get("jsonBody");
                if (!(jsonBodyExpected instanceof Map)) {
                    logger.error("测试用例 {} 的expected.jsonBody必须是Map类型", caseData);
                    throw new IllegalArgumentException("测试用例的expected.jsonBody必须是Map类型");
                }
                
                // 判断响应是否为JSON格式
                if (isJsonResponse(response.getBody())) {
                    // JSON格式响应断言
                    assertJsonResponse((Map<String, Object>) jsonBodyExpected, response.getBody());
                } else {
                    // 如果响应不是JSON格式，直接报错
                    fail("响应不是有效的JSON格式: " + response.getBody());
                }
            }
        } catch (Exception e) {
            logger.error("断言过程中发生错误: ", e);
            fail("断言过程中发生错误: " + e.getMessage());
        }
    }

    /**
     * 判断响应是否为JSON格式
     *
     * @param response 响应字符串
     * @return 是否为JSON格式
     */
    private static boolean isJsonResponse(String response) {
        if (response == null || response.trim().isEmpty()) {
            return false;
        }
        
        response = response.trim();
        return (response.startsWith("{") && response.endsWith("}")) ||
               (response.startsWith("[") && response.endsWith("]"));
    }

    /**
     * 断言JSON格式响应
     *
     * @param expected 预期结果
     * @param response 实际响应字符串
     */
    private static void assertJsonResponse(Map<String, Object> expected, String response) {
        try {
            JsonNode jsonResponse = mapper.readTree(response);

            for (Map.Entry<String, Object> entry : expected.entrySet()) {
                String key = entry.getKey();
                Object expectedValue = entry.getValue();

                // 从响应中获取对应字段的值
                JsonNode actualNode = jsonResponse.at("/" + key);
                if (!actualNode.isMissingNode()) {
                    Object actualValue = getNodeValue(actualNode);
                    assertEquals(actualValue, expectedValue,
                            "断言失败: 字段 '" + key + "' 预期值 '" + expectedValue + "' 但实际值 '" + actualValue + "'");
                } else {
                    fail("断言失败: 在响应中找不到字段 '" + key + "'");
                }
            }
        } catch (Exception e) {
            logger.error("JSON断言过程中发生错误: ", e);
            fail("JSON断言过程中发生错误: " + e.getMessage());
        }
    }


    /**
     * 获取JsonNode的值
     *
     * @param node JsonNode节点
     * @return 节点值
     */
    private static Object getNodeValue(JsonNode node) {
        if (node.isTextual()) {
            return node.asText();
        } else if (node.isNumber()) {
            return node.asDouble();
        } else if (node.isBoolean()) {
            return node.asBoolean();
        } else if (node.isNull()) {
            return null;
        } else {
            return node.toString();
        }
    }
}